<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>空间音频与3D互动</title>
  <style>
    body {
      margin: 0;
    }

    canvas {
      display: block;
    }
    #positionDisplay {
            position: absolute;
            top: 10px;
            left: 10px;
            color: white;
            background: rgba(0, 0, 0, 0.5);
            padding: 5px;
            border-radius: 5px;
        }
  </style>
</head>

<body>
  <div id="positionDisplay">位置: (0, 0, 0)</div>
  <script src="https://threejs.org/build/three.js"></script>
  <script>
    let camera, scene, renderer;
    let audioListener, audioSource, panner;
    let sourceSphere, listenerSphere, boundarySphere;
    const audioContext = new AudioContext();
    const boundaryRadius = 10;
    const movementSpeed = 0.1;
    let positionDisplay;

    function initThreeJS() {
      positionDisplay = document.getElementById('positionDisplay');
      scene = new THREE.Scene();
      camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
      renderer = new THREE.WebGLRenderer();
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);

      // 添加球体代表音源
      const sourceGeometry = new THREE.SphereGeometry(0.5, 32, 32);
      const sourceMaterial = new THREE.MeshBasicMaterial({
        color: 0xff0000
      });
      sourceSphere = new THREE.Mesh(sourceGeometry, sourceMaterial);
      scene.add(sourceSphere);

      // 添加球体代表监听者
      const listenerGeometry = new THREE.SphereGeometry(0.5, 32, 32);
      const listenerMaterial = new THREE.MeshBasicMaterial({
        color: 0x0000ff
      });
      listenerSphere = new THREE.Mesh(listenerGeometry, listenerMaterial);
      listenerSphere.position.set(5, 0, 0);
      scene.add(listenerSphere);

      // 添加边界球体
      const boundaryGeometry = new THREE.SphereGeometry(boundaryRadius, 32, 32);
      const boundaryMaterial = new THREE.MeshBasicMaterial({
        color: 0x00ff00,
        wireframe: true
      });
      boundarySphere = new THREE.Mesh(boundaryGeometry, boundaryMaterial);
      scene.add(boundarySphere);

      camera.position.z = 15;

      // 添加键盘事件的逻辑
      document.addEventListener('keydown', onDocumentKeyDown, false);
    }

    function initAudio() {
      audioListener = audioContext.listener;

      // 创建PannerNode
      panner = audioContext.createPanner();
      panner.panningModel = 'HRTF';
      panner.distanceModel = 'inverse';
      panner.setPosition(sourceSphere.position.x, sourceSphere.position.y, sourceSphere.position.z);

      // 加载音频并播放
      const audioSource = audioContext.createBufferSource();
      loadAudio('./sound/test.mp3').then(audioBuffer => {
        audioSource.buffer = audioBuffer;
        audioSource.connect(panner).connect(audioContext.destination);
        audioSource.start();
      });
    }

    function loadAudio(url) {
      return fetch(url)
        .then(response => response.arrayBuffer())
        .then(arrayBuffer => audioContext.decodeAudioData(arrayBuffer));
    }
    function updatePositionDisplay(position) {
            positionDisplay.textContent = `位置: (${position.x.toFixed(2)}, ${position.y.toFixed(2)}, ${position.z.toFixed(2)})`;
        }
    function animate() {
      requestAnimationFrame(animate);
      renderer.render(scene, camera);

      // 更新Web Audio API中的监听者位置
      const listenerPos = listenerSphere.position;
      audioListener.setPosition(listenerPos.x, listenerPos.y, listenerPos.z);
      // 更新显示位置
      updatePositionDisplay(listenerPos);
    }

    function onDocumentKeyDown(event) {
      switch (event.keyCode) {
        case 38: // Up arrow
          moveListenerSphere(0, movementSpeed, 0);
          break;
        case 40: // Down arrow
          moveListenerSphere(0, -movementSpeed, 0);
          break;
        case 37: // Left arrow
          moveListenerSphere(-movementSpeed, 0, 0);
          break;
        case 39: // Right arrow
          moveListenerSphere(movementSpeed, 0, 0);
          break;
        case 87: // W
          moveListenerSphere(0, 0, movementSpeed);
          break;
        case 83: // S
          moveListenerSphere(0, 0, -movementSpeed);
          break;
      }
    }

    function moveListenerSphere(x, y, z) {
      const newPosition = listenerSphere.position.clone().add(new THREE.Vector3(x, y, z));
      if (newPosition.length() <= boundaryRadius) {
        listenerSphere.position.copy(newPosition);
      }
    }
    initThreeJS();
    initAudio();
    animate();
  </script>
</body>

</html>